﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using DriveBeamEditor.Enums;
using DriveBeamEditor.Interfaces;
using JBeamLibrary.Tools;

namespace DriveBeamEditor.Controls.General
{
    public partial class NumTextBox : TextBox, IEditor, IValueEditor
    {
        #region Fields

        private NumericMaskTypes _maskType = NumericMaskTypes.Float;
        private object _editValue = null;
        private bool _isValueValid = true;
        private Color _validBackColor = Color.White;
        private Color _validForeColor = Color.Black;
        private Color _invalidBackColor = Color.Red;
        private Color _invalidForeColor = Color.White;

        public event EventHandler EditValueChanged;
        public event EventHandler MaskTypeChanged;

        #endregion

        #region Properties

        public object EditValue
        {
            get { return _editValue; }
            set
            {
                if (_editValue.SafeEquals(value)) return;
                _editValue = value;
                if (IsMax) _editValue = "MAX";
                else if (IsNull) _editValue = "NULL";
                OnEditValueChanged();
            }
        }

        public NumericMaskTypes MaskType
        {
            get { return _maskType; }
            set
            {
                if (_maskType == value) return;
                _maskType = value;
                OnMaskTypeChanged();
            }
        }

        public Color ValidBackColor
        {
            get { return _validBackColor; }
            set
            {
                _validBackColor = value;
                UpdateDisplay();
            }
        }

        public Color ValidForeColor
        {
            get { return _validForeColor; }
            set
            {
                _validForeColor = value;
                UpdateDisplay();
            }
        }

        public Color InvalidBackColor
        {
            get { return _invalidBackColor; }
            set
            {
                _invalidBackColor = value;
                UpdateDisplay();
            }
        }

        public Color InvalidForeColor
        {
            get { return _invalidForeColor; }
            set
            {
                _invalidForeColor = value;
                UpdateDisplay();
            }
        }

        public bool IsValueValid
        {
            get { return _isValueValid; }
        }

        public bool IsMax
        {
            get { return /*EditValue.SafeEquals("MAX") || */NumericMasks.RangeMap[MaskType].Value.Equals(EditValue); }
        }

        public bool IsNull
        {
            get { return /*EditValue.SafeEquals("NULL") || */NumericMasks.RangeMap[MaskType].Key.Equals(EditValue); }
        }

        #endregion

        #region CTORs

        public NumTextBox()
        {
            InitializeComponent();
        }

        #endregion

        #region Events

        protected override void OnTextChanged(EventArgs e)
        {
            if (Text.ToUpperInvariant().Equals("MAX"))
            {
                if (!Text.Equals("MAX"))
                {
                    Text = @"MAX"; // force upper case
                    return;
                }
                _editValue = NumericMasks.RangeMap[MaskType].Value;
                _isValueValid = true;
                UpdateDisplay();
                return;
            }

            if (Text.ToUpperInvariant().Equals("NULL"))
            {
                if (!Text.Equals("NULL"))
                {
                    Text = @"NULL"; // force upper case
                    return;
                }
                _editValue = NumericMasks.RangeMap[MaskType].Key;
                _isValueValid = true;
                UpdateDisplay();
                return;
            }

            var rep = Regex.Replace(Text, @"[^0-9\.\-,]*", "", RegexOptions.IgnoreCase);
            if (!Text.Equals(rep))
            {
                Text = rep;
                SelectionStart++;
                return;
            }

            if (Text.StartsWith("."))
            {
                Text = string.Format("0{0}", Text);
                return;
            }

            if (Text.Contains(","))
            {
                Text = Text.Replace(',', '.');
                return;
            }

            if (NumericMasks.RegExMap[MaskType].Contains(@"\.") && !Text.Contains("."))
            {
                Text += @".0";
                SelectionStart++;
                return;
            }

            if (Text.Length > SelectionStart && Text[SelectionStart] == '.' && Text[SelectionStart - 1] == '.')
            {
                var sel = SelectionStart;
                Text = Text.Remove(SelectionStart, 1);
                SelectionStart = sel;
                return;
            }

            base.OnTextChanged(e);

            object newValue;
            _isValueValid = NumericMasks.ValidateValue(MaskType, Text, out newValue);

            var isNew = !newValue.Equals(EditValue);
            _editValue = newValue;

            UpdateDisplay();

            if (isNew && EditValueChanged != null)
                EditValueChanged(this, e);
        }

        protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
        {
            if (e.KeyCode == Keys.Tab)
                SelectNextEditor();
            else
                base.OnPreviewKeyDown(e);
        }

        private void contextMenu_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            if (e.ClickedItem == itemSetNULL)
                SetToNull();
            else if (e.ClickedItem == itemSetMAX)
                SetToMax();
            else if (e.ClickedItem == itemDefault)
                SetDefault();
        }

        #endregion

        #region Methods

        public void SetToMax()
        {
            EditValue = "MAX";
        }

        public void SetToNull()
        {
            EditValue = "NULL";
        }

        public void SetDefault()
        {
            EditValue = NumericMasks.DefaultsMap[MaskType];
        }

        public void SetEditValueSilent(object value)
        {
            object newValue;
            _isValueValid = NumericMasks.ValidateValue(MaskType, Text, out newValue);
            _editValue = newValue;

            UpdateDisplay();
        }

        private void UpdateDisplay()
        {
            BackColor = _isValueValid ? ValidBackColor : InvalidBackColor;
            ForeColor = _isValueValid ? ValidForeColor : InvalidForeColor;
        }

        private void OnEditValueChanged()
        {
            Text = EditValue.ToStringOrZero();

            if (EditValueChanged != null)
                EditValueChanged(this, new EventArgs());
        }

        private void OnMaskTypeChanged()
        {
            object newValue;
            _isValueValid = NumericMasks.ValidateValue(MaskType, Text, out newValue);

            var isNew = !newValue.Equals(EditValue);
            _editValue = newValue;

            UpdateDisplay();

            if (isNew && EditValueChanged != null)
                EditValueChanged(this, new EventArgs());

            if (MaskTypeChanged != null)
                MaskTypeChanged(this, new EventArgs());
        }

        private void SelectNextEditor(Control parent = null)
        {
            var nextControl = (parent ?? Parent).Controls.OfType<Control>().FirstOrDefault(ctrl => ctrl != this && ctrl.TabIndex > TabIndex && ctrl is IEditor);
            if (nextControl != null)
            {
                nextControl.Focus();
                return;
            }

            var keyValueCtrl = GetKeyValueControl(parent ?? Parent);
            if (keyValueCtrl == null) return;

            var nextKeyValueControl = keyValueCtrl.Parent.Controls.OfType<KeyValueControl>().OrderBy(kvc => kvc.TabIndex).FirstOrDefault(kvc => kvc.TabIndex > keyValueCtrl.TabIndex);
            if (nextKeyValueControl == null)
            {
                SelectNextEditor(keyValueCtrl.Parent);
                return;
            }

            var editor = GetEditor(nextKeyValueControl);
            if (!(editor is Control)) return;
            ((Control)editor).Select();
            if (editor is TextBox) ((TextBox)editor).SelectAll();
        }

        private static KeyValueControl GetKeyValueControl(Control ctrl)
        {
            if (ctrl is KeyValueControl) return ctrl as KeyValueControl;
            return ctrl.Parent != null ? GetKeyValueControl(ctrl.Parent) : null;
        }

        private static IEditor GetEditor(Control ctrl)
        {
            if (ctrl is IValueEditor) return ctrl as IEditor;
            return ctrl.Controls.OfType<Control>().Select(GetEditor).FirstOrDefault(editor => editor != null);
        }

        #endregion
    }
}
